﻿namespace GenericListProgram
{
    using System;

    public class GenericList<T>
    {
        private T[] elementsArray;

        public GenericList(int capacity)
        {
            if (capacity < 0)
            {
                throw new ArgumentOutOfRangeException("Non-negative number required.");
            }

            this.Count = 0;
            this.Capacity = capacity;
            this.elementsArray = new T[capacity];
        }

        public int Count { get; private set; }

        public int Capacity
        {
            get
            {
                return this.elementsArray.Length;
            }

            set
            {
                if (value <= 0 || value < this.Count)
                {
                    throw new ArgumentOutOfRangeException();
                }

                if (this.elementsArray != null && this.elementsArray.Length >= 0)
                {
                    T[] newArray = new T[value];
                    Array.Copy(this.elementsArray, 0, newArray, 0, this.Count);

                    this.elementsArray = newArray;
                }
            }
        }

        //private T[] ElementsArray
        //{
        //    get
        //    {
        //        return this.elementsArray;
        //    }

        //    set
        //    {
        //        if (this.Count < this.Capacity)
        //        {
        //            this.elementsArray = value;
        //        }
        //        else
        //        {
        //            T[] newArray = new T[this.Capacity * 2];
        //            Array.Copy(this.elementsArray, newArray, this.Count);
        //            this.elementsArray = newArray;
        //        }
        //    }
        //}

        public T this[int index]
        {
            get
            {
                if (index >= this.Count || index < 0)
                {
                    throw new IndexOutOfRangeException();
                }
                else
                {
                    return this.elementsArray[index];
                }
            }

            set
            {
                if (index >= this.Count || index < 0)
                {
                    throw new IndexOutOfRangeException();
                }

                this.elementsArray[index] = value;
            }
        }

        public override string ToString()
        {
            if (this.Count != 0)
            {
                return string.Join(", ", this.elementsArray);
            }
            else
            {
                return string.Empty;
            }
        }

        public void Add(T element)
        {
            int arrayLength = this.Count;
            if (arrayLength >= this.Capacity)
            {
                this.Capacity *= 2;
            }

            this.elementsArray[arrayLength] = element;
            this.Count++;
        }

        public void RemoveAt(int index)
        {
            if (index < 0 || index > this.Count)
            {
                throw new ArgumentOutOfRangeException();
            }

            T[] newArray = new T[this.Count - 1];
            int iter = 0;
            for (int i = 0; i < this.Count; i++)
            {
                if (i != index)
                {
                    newArray[iter] = this.elementsArray[i];
                    iter++;
                }
            }

            this.Count--;
            this.elementsArray = newArray;
        }

        public void InsertAt(int index, T element)
        {
            if (index < 0) //|| index > this.Count)
            {
                throw new ArgumentOutOfRangeException();
            }

            if (index >= this.Count)
            {
                this.Capacity *= 2;
            }

            T[] newArray = new T[this.Count + 1];
            int iter = 0;
            for (int i = 0; i <= this.Count; i++)
            {
                if (i != index)
                {
                    newArray[i] = this.elementsArray[iter];
                    iter++;
                }
                else
                {
                    newArray[i] = element;
                }
            }

            this.Count++;
            this.elementsArray = newArray;
        }

        public void Clear()
        {
            Array.Clear(this.elementsArray, 0, this.Count);
            this.Count = 0;
        }

        public int FindIndex(Predicate<T> match)
        {
            int index = 0;
            while (index < this.Count)
            {
                if (match(this.elementsArray[index]))
                {
                    return index;
                }
                else
                {
                    index++;
                }
            }

            return -1;
        }

        //public T Min<T>()
        //{

        //}

        //public T Max<T>()
        //    where T : IComparable, new()
        //{
        //    if (this.Count == 0)
        //    {
        //        throw new InvalidOperationException("Sequence must not be empty.");
        //    }
        //    else if (this.Count == 1)
        //    {
        //        return this.elementsArray[0];
        //    }

        //}
    }
}
